More fixes to HVM FPU management. Mostly for SVM, but also
authorkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Tue, 14 Feb 2006 23:15:11 +0000 (00:15 +0100)
committerkaf24@firebug.cl.cam.ac.uk <kaf24@firebug.cl.cam.ac.uk>
Tue, 14 Feb 2006 23:15:11 +0000 (00:15 +0100)
fix VMX mov_to_cr0: must setup_fpu() if the new cr0 value
has TS clear, regardless of previous cr0.TS value.

Signed-off-by: Keir Fraser <keir@xensource.com>
xen/arch/x86/hvm/svm/svm.c
xen/arch/x86/hvm/svm/vmcb.c
xen/arch/x86/hvm/svm/x86_32/exits.S
xen/arch/x86/hvm/vmx/vmx.c
xen/arch/x86/traps.c
xen/include/asm-x86/i387.h

index 830d03921a3720527e6a6ae6ff91f5d9c9c60d59..14f04bd0a80a919a8c4dbcf742259eb992a2ac2b 100644 (file)
@@ -701,12 +701,21 @@ void svm_stts(struct vcpu *v)
 {
     struct vmcb_struct *vmcb = v->arch.hvm_svm.vmcb;
 
-    ASSERT(vmcb);    
-
-    vmcb->cr0 |= X86_CR0_TS;
+    /* FPU state already dirty? Then no need to setup_fpu() lazily. */
+    if ( test_bit(_VCPUF_fpu_dirtied, &v->vcpu_flags) )
+        return;
 
-    if (!(v->arch.hvm_svm.cpu_shadow_cr0 & X86_CR0_TS))
+    /*
+     * If the guest does not have TS enabled then we must cause and handle an 
+     * exception on first use of the FPU. If the guest *does* have TS enabled 
+     * then this is not necessary: no FPU activity can occur until the guest 
+     * clears CR0.TS, and we will initialise the FPU when that happens.
+     */
+    if ( !(v->arch.hvm_svm.cpu_shadow_cr0 & X86_CR0_TS) )
+    {
         v->arch.hvm_svm.vmcb->exception_intercepts |= EXCEPTION_BITMAP_NM;
+        vmcb->cr0 |= X86_CR0_TS;
+    }
 }
 
 static void arch_svm_do_launch(struct vcpu *v) 
@@ -884,14 +893,11 @@ static void svm_do_no_device_fault(struct vmcb_struct *vmcb)
 {
     struct vcpu *v = current;
 
-    clts();
-
     setup_fpu(v);    
+    vmcb->exception_intercepts &= ~EXCEPTION_BITMAP_NM;
 
-    if (!(v->arch.hvm_svm.cpu_shadow_cr0 & X86_CR0_TS))
+    if ( !(v->arch.hvm_svm.cpu_shadow_cr0 & X86_CR0_TS) )
         vmcb->cr0 &= ~X86_CR0_TS;
-    
-    vmcb->exception_intercepts &= ~EXCEPTION_BITMAP_NM;
 }
 
 
@@ -1350,10 +1356,11 @@ static int svm_set_cr0(unsigned long value)
     vmcb->cr0 = value | X86_CR0_PG;
     v->arch.hvm_svm.cpu_shadow_cr0 = value;
 
-    /* Check if FP Unit Trap need to be on */
-    if (value & X86_CR0_TS)
-    { 
-       vmcb->exception_intercepts |= EXCEPTION_BITMAP_NM;
+    /* TS cleared? Then initialise FPU now. */
+    if ( !(value & X86_CR0_TS) )
+    {
+        setup_fpu(v);
+        vmcb->exception_intercepts &= ~EXCEPTION_BITMAP_NM;
     }
 
     HVM_DBG_LOG(DBG_LEVEL_VMMU, "Update CR0 value = %lx\n", value);
@@ -1669,11 +1676,11 @@ static int svm_cr_access(struct vcpu *v, unsigned int cr, unsigned int type,
         break;
 
     case INSTR_CLTS:
-        clts();
+        /* TS being cleared means that it's time to restore fpu state. */
         setup_fpu(current);
+        vmcb->exception_intercepts &= ~EXCEPTION_BITMAP_NM;
         vmcb->cr0 &= ~X86_CR0_TS; /* clear TS */
         v->arch.hvm_svm.cpu_shadow_cr0 &= ~X86_CR0_TS; /* clear TS */
-        vmcb->exception_intercepts &= ~EXCEPTION_BITMAP_NM;
         break;
 
     case INSTR_LMSW:
@@ -1803,11 +1810,8 @@ static inline void svm_vmexit_do_hlt(struct vmcb_struct *vmcb)
     struct vcpu *v = current;
     struct hvm_virpit *vpit = &v->domain->arch.hvm_domain.vpit;
     s_time_t  next_pit = -1, next_wakeup;
-    unsigned int inst_len;
 
-    svm_stts(v);
-    inst_len = __get_instruction_length(vmcb, INSTR_HLT, NULL);
-    __update_guest_eip(vmcb, inst_len);
+    __update_guest_eip(vmcb, 1);
 
     if ( !v->vcpu_id )
         next_pit = get_pit_scheduled(v, vpit);
@@ -1822,7 +1826,6 @@ static inline void svm_vmexit_do_hlt(struct vmcb_struct *vmcb)
 
 static inline void svm_vmexit_do_mwait(void)
 {
-    return;
 }
 
 
@@ -2494,7 +2497,6 @@ asmlinkage void svm_vmexit_handler(struct cpu_user_regs regs)
         break;
 
     case VMEXIT_INTR:
-        svm_stts(v);
         raise_softirq(SCHEDULE_SOFTIRQ);
         break;
 
index 5d45a4c25e4e3800f3bcf755a1902c15c75e8abd..51253ee666872fca7bba7a0b776b99e7fa15e5ec 100644 (file)
@@ -489,7 +489,9 @@ void svm_do_resume(struct vcpu *v)
 {
     struct domain *d = v->domain;
     struct hvm_virpit *vpit = &d->arch.hvm_domain.vpit;
-    
+
+    svm_stts(v);
+
     if ( test_bit(iopacket_port(d), &d->shared_info->evtchn_pending[0]) ||
          test_bit(ARCH_HVM_IO_WAIT, &v->arch.hvm_vcpu.ioflags) )
         hvm_wait_io();
@@ -498,7 +500,7 @@ void svm_do_resume(struct vcpu *v)
     if ( vpit->first_injected )
         pickup_deactive_ticks(vpit);
     svm_set_tsc_shift(v, vpit);
-    
+
     /* We can't resume the guest if we're waiting on I/O */
     ASSERT(!test_bit(ARCH_HVM_IO_WAIT, &v->arch.hvm_vcpu.ioflags));
 }
index 33a027407879c560a5263bab64ef8c2898e7c33a..7c6147269af6770dcd32399a6b99f9a5184a022c 100644 (file)
@@ -89,7 +89,7 @@
 #define CLGI   .byte 0x0F,0x01,0xDD
 
 #define DO_TSC_OFFSET 0
-#define DO_FPUSAVE    1
+#define DO_FPUSAVE    0
         
 ENTRY(svm_asm_do_launch)
         sti
index 1068ae0c66d031f003327cc3bcc1e3d00123c840..67e6f21835cfeb3774c6bc66cbf6052c47fb627a 100644 (file)
@@ -615,8 +615,8 @@ static void vmx_do_no_device_fault(void)
     unsigned long cr0;
     struct vcpu *v = current;
 
-    clts();
     setup_fpu(current);
+    __vm_clear_bit(EXCEPTION_BITMAP, EXCEPTION_BITMAP_NM);
 
     /* Disable TS in guest CR0 unless the guest wants the exception too. */
     __vmread_vcpu(v, CR0_READ_SHADOW, &cr0);
@@ -626,9 +626,6 @@ static void vmx_do_no_device_fault(void)
         cr0 &= ~X86_CR0_TS;
         __vmwrite(GUEST_CR0, cr0);
     }
-
-    /* Xen itself doesn't need another exception. */
-    __vm_clear_bit(EXCEPTION_BITMAP, EXCEPTION_BITMAP_NM);
 }
 
 /* Reserved bits: [31:15], [12:11], [9], [6], [2:1] */
@@ -1158,14 +1155,11 @@ static int vmx_set_cr0(unsigned long value)
     __vmread_vcpu(v, CR0_READ_SHADOW, &old_cr0);
     paging_enabled = (old_cr0 & X86_CR0_PE) && (old_cr0 & X86_CR0_PG);
 
-    /*
-     * Disable TS? Then we do so at the same time, and initialise FPU.
-     * This avoids needing another vmexit.
-     */
-    if ( (old_cr0 & ~value & X86_CR0_TS) != 0 )
+    /* TS cleared? Then initialise FPU now. */
+    if ( !(value & X86_CR0_TS) )
     {
-        clts();
         setup_fpu(v);
+        __vm_clear_bit(EXCEPTION_BITMAP, EXCEPTION_BITMAP_NM);
     }
 
     __vmwrite(GUEST_CR0, value | X86_CR0_PE | X86_CR0_PG | X86_CR0_NE);
@@ -1520,8 +1514,8 @@ static int vmx_cr_access(unsigned long exit_qualification, struct cpu_user_regs
         TRACE_VMEXIT(1,TYPE_CLTS);
 
         /* We initialise the FPU now, to avoid needing another vmexit. */
-        clts();
-        setup_fpu(current);
+        setup_fpu(v);
+        __vm_clear_bit(EXCEPTION_BITMAP, EXCEPTION_BITMAP_NM);
 
         __vmread_vcpu(v, GUEST_CR0, &value);
         value &= ~X86_CR0_TS; /* clear TS */
index f5821ef1953994f075dbb9ba18e8c4f279e77f72..3a1deb85357c02abbde961f5785b8662396e6a66 100644 (file)
@@ -1261,9 +1261,6 @@ asmlinkage int math_state_restore(struct cpu_user_regs *regs)
     struct trap_bounce *tb;
     struct trap_info *ti;
 
-    /* Prevent recursion. */
-    clts();
-
     setup_fpu(current);
 
     if ( current->arch.guest_context.ctrlreg[0] & X86_CR0_TS )
index f9216605ed85e7779c790616ebc50aec23bef618..fa9ba43e90e92091befee8b2a7bc3bdf294412b2 100644 (file)
@@ -33,6 +33,7 @@ static inline void setup_fpu(struct vcpu *v)
 {
     if ( !test_and_set_bit(_VCPUF_fpu_dirtied, &v->vcpu_flags) )
     {
+        clts();
         if ( test_bit(_VCPUF_fpu_initialised, &v->vcpu_flags) )
             restore_fpu(v);
         else